﻿
Imports std

'make timer3 also global to avoid break any existing logic
Public Module _timer3
    Public ReadOnly timer3 As timerSet = Nothing

    Sub New()
        timer3 = New timerSet()
    End Sub
End Module

Namespace timer3Unittests
    Public Module timer3Unittests
        Private Class guidwrapper
            Public guid As Guid = guid.Empty
        End Class

        Private Sub unittestCallback(ByRef obj As Object)
            If TypeOf obj Is guidwrapper Then
                Dim gw As guidwrapper = Nothing
                gw = convertor.cast(Of guidwrapper)(obj)
                sleep(rnd(0, 1000, True))
                While gw.guid = Guid.Empty
                    sleep(16)
                End While
                If Not _timer3.timer3.removecall(gw.guid) Then
                    raiseError("caught an invalid removecall in callback, guid = " + Convert.ToString(gw.guid) _
                               , errorHandle.errorType.exclamation)
                    pause()
                Else
                    raiseError("remove call in callback " + Convert.ToString(gw.guid))
                End If
            Else
                sleep(rnd(0, 16, True))
                obj += 1
            End If
        End Sub

        Private Sub unittestThread()
            Dim i As Int64
            For i = 0 To 1000
                If rnd(0, 10, True) < 3 Then
                    Dim gw As guidwrapper = New guidwrapper()
                    gw.guid = _timer3.timer3.addcall(AddressOf unittestCallback, 10000, gw)
                    raiseError("add call as guidwrapper " + Convert.ToString(gw.guid))
                Else
                    Dim guid As Guid = guid.Empty
                    Dim count As Int64
                    guid = _timer3.timer3.addcall(AddressOf unittestCallback, 64, count)
                    raiseError("add call " + Convert.ToString(guid))
                    While count < rnd(0, 10, True)
                        sleep(64)
                    End While
                    If Not _timer3.timer3.removecall(guid) Then
                        raiseError("caught an invalid removecall, guid = " + Convert.ToString(guid) _
                                   , errorHandle.errorType.exclamation)
                        pause()
                    Else
                        raiseError("remove call " + Convert.ToString(guid))
                    End If
                End If
            Next
        End Sub

        Public Sub unittest()
            Dim th As Threading.Thread = Nothing
            Dim i As Int64
            For i = 0 To 3
                th = New Threading.Thread(AddressOf unittestThread)
                th.Start()
            Next

            th.Join()
        End Sub
    End Module
End Namespace

Public Class timerSet
#If PocketPC OrElse Smartphone Then
    public const defaultThreadCount as int64 = 8
#Else
    Public Shared ReadOnly defaultThreadCount As Int64 = Convert.ToInt64(Environment.ProcessorCount()) << 3
#End If
    Private timers As vector(Of timer2) = Nothing

    Public Sub [stop]()
        Dim i As Int64
        i = 0
        While i < timers.size()
            timers(i).stop()
            i += 1
        End While
    End Sub

    Public Property threadCount() As Int64
        Get
            Return timers.size()
        End Get
        Set(ByVal value As Int64)
            'cannot remove threads now
            While timers.size() < value
                timers.push_back(New timer2())
            End While
        End Set
    End Property

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick, ByVal intervalMS As UInt64) As Guid
        Dim index As Int64
        index = rnd(0, threadCount(), True)
        Return addcall(tickfunc, intervalMS, index)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick2, ByVal intervalMS As UInt64) As Guid
        Dim index As Int64
        index = rnd(0, threadCount(), True)
        Return addcall(tickfunc, intervalMS, index)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick3, ByVal intervalMS As UInt64, _
                            ByRef obj As Object) As Guid
        Dim index As Int64
        index = rnd(0, threadCount(), True)
        Return addcall(tickfunc, intervalMS, obj, index)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick4, ByVal intervalMS As UInt64, _
                            ByRef obj As Object) As Guid
        Dim index As Int64
        index = rnd(0, threadCount(), True)
        Return addcall(tickfunc, intervalMS, obj, index)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick, ByVal intervalMS As UInt64, _
                            ByVal index As Int64) As Guid
        If index < 0 OrElse index >= threadCount() Then
            Return Guid.Empty
        End If
        Return timers(index).addcall(tickfunc, intervalMS)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick2, ByVal intervalMS As UInt64, _
                            ByVal index As Int64) As Guid
        If index < 0 OrElse index >= threadCount() Then
            Return Guid.Empty
        End If
        Return timers(index).addcall(tickfunc, intervalMS)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick3, ByVal intervalMS As UInt64, _
                            ByRef obj As Object, ByVal index As Int64) As Guid
        If index < 0 OrElse index >= threadCount() Then
            Return Guid.Empty
        End If
        Return timers(index).addcall(tickfunc, intervalMS, obj)
    End Function

    Public Function addcall(ByVal tickfunc As timer2.timer2Tick4, ByVal intervalMS As UInt64, _
                            ByRef obj As Object, ByVal index As Int64) As Guid
        If index < 0 OrElse index >= threadCount() Then
            Return Guid.Empty
        End If
        Return timers(index).addcall(tickfunc, intervalMS, obj)
    End Function

    Public Function removecall(ByVal guid As Guid) As Boolean
        Dim i As Int64
        For i = 0 To threadCount() - 1
            If timers(i) Is Nothing Then
                Exit For
                'when finialize, cannot confirm which object is still available
            End If
            If timers(i).availableGuid(guid) Then
                assert(timers(i).removecall(guid), "cannot remove call with guid " + Convert.ToString(guid))
                Exit For
            End If
        Next

        Return i < threadCount()
    End Function

    Public Sub New()
        timers = New vector(Of timer2)()
        threadCount() = defaultThreadCount
    End Sub
End Class
